package com.honey.compilation.sql.ddl;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;

import com.honey.compilation.sql.SqlColumn;
import com.honey.compilation.sql.SqlConstraint;
import com.honey.compilation.sql.SqlKeyWord;
import com.honey.compilation.sql.SqlScript;
import com.honey.compilation.sql.SqlTable;
import com.honey.compilation.sql.SqlType;
import com.honey.core.builder.CharacterBuilder;
import com.honey.core.builder.CharacterBuilderFactory;
import com.honey.core.compiler.CompilationException;
import com.honey.core.compiler.IndentSpace;
import com.honey.core.utils.StringUtility;

/**
 * 下面是摘录SQL 92对Create Table的定义:
 * &lt;table definition&gt;    ::= <br />
 *   CREATE [ { GLOBAL | LOCAL } TEMPORARY ] TABLE &lt;table name&gt; &lt;table element list&gt; [ ON COMMIT { DELETE | PRESERVE } ROWS ] <br/>
 * &lt;table name&gt;    ::=   &lt;qualified name&gt;<br/>
 * &lt;qualified name&gt;    ::=   [ &lt;schema name&gt; &lt;period&gt; ]<br />
 * &lt;table element list&gt;    ::=   &lt;left paren&gt; &lt;table element&gt; [ { &lt;comma&gt; &lt;table element&gt; }... ] &lt;right paren&gt;<br />
 * &lt;table element&gt;    ::=   &lt;column definition&gt; | &lt;table constraint definition&gt;<br />
 * &lt;column definition&gt;    ::= <br />
 * &lt;column name&gt; { &lt;data type&gt; | &lt;domain name&gt; } [ &lt;default clause&gt; ] [ &lt;column constraint definition&gt; ... ] [ &lt;collate clause&gt; ]<br />
 * &lt;column constraint definition&gt;    ::=
 * [ &lt;constraint name definition&gt; ] &lt;column constraint&gt; [ &lt;constraint attributes&gt; ]<br />
 * &lt;constraint name definition&gt;    ::=   CONSTRAINT &lt;constraint name&gt;<br />
 * &lt;column constraint&gt;    ::=NOT NULL |     &lt;unique specification&gt;<br />
 * &lt;unique specification&gt;    ::=   UNIQUE | PRIMARY KEY<br />
 * &lt;table constraint definition&gt;    ::=   [ &lt;constraint name definition&gt; ] &lt;table constraint&gt; [ &lt;constraint check time&gt; ]<br />
 * &lt;table constraint&gt;    ::=&lt;unique constraint definition&gt;<br />
 * &lt;unique constraint definition&gt;    ::=   &lt;unique specification&gt; &lt;left paren&gt; &lt;unique column list&gt; &lt;right paren&gt;<br />
 * &lt;unique column list&gt;    ::=   &lt;column name list&gt;<br />
 * &lt;column name list&gt;    ::=   &lt;column name&gt; [ { &lt;comma&gt; &lt;column name&gt; }... ]<br />
 * 在上述的定义中可以看到SQL 92对字段的约束只有NOT NULL | UNIQUE | PRIMARY KEY 三种种<br />
 * 本类只实现SQL 92描述中最常用的定义.
 * 
 * @author Administrator
 *
 */
public class CreateTable extends Create implements SqlScript {

	private SqlTable table ;
	
	/** 附加信息 */
	private String append;
	
	/** 列 */
	private java.util.Set<SqlColumn> columns ;
	
	/**
	 * 构造函数
	 * @param name 表名称
	 */
	public CreateTable(String name) {
		this(null, name);
	}
	
	/**
	 * 构造函数
	 * @param table 数据库表对象
	 */
	public CreateTable(SqlTable table){
		this.table= table;
		columns= new LinkedHashSet<SqlColumn>();
	}
	
	/**
	 * 构造函数
	 * @param schema 数据库结构
	 * @param name 表名称
	 */
	public CreateTable(String schema, String name) {
		this.table = new SqlTable(schema,name);
		columns= new LinkedHashSet<SqlColumn>();
	}

	/**
	 * 获取表名称
	 * @return
	 */
	public String getName() {
		return table.getTableName();
	}

	/**
	 * 获取数据库结构名称
	 * @return
	 */
	public String getSchema() {
		return table.getSchema();
	}

	/**
	 * 设定数据结构名称
	 * @param schema 据结构名称 
	 */
	public void setSchema(String schema) {
		this.table.setSchema(schema);
	}
	
	/**
	 * 获取附加信息
	 * @return
	 */
	public String getAppend() {
		return append;
	}

	/**
	 * 设定附加信息
	 * @param append
	 */
	public void setAppend(String append) {
		this.append = append;
	}

	/**
	 * 添加字段
	 * @param columnName 列名称
	 * @param jdbcType 在java.sql.Types类中指定类型
	 * @return
	 */
	public CreateTable addColumn(String columnName, int jdbcType){
		addColumn(new SqlColumn(columnName , jdbcType)) ;
		
		return this;
	}
	
	/**
	 * 添加字段
	 * @param columnName 列名称
	 * @param jdbcType 在java.sql.Types类中指定类型
	 * @return
	 */
	public CreateTable addColumn(String columnName, int jdbcType, SqlConstraint constraint){
		ExtendConstraintColumn column= new ExtendConstraintColumn(columnName , jdbcType) ;
		column.addSqlConstraint(constraint) ;
		addColumn( column ) ;

		return this;
	}
	
	/**
	 * 添加字段
	 * @param columnName 列名称
	 * @param jdbcType 在java.sql.Types类中指定类型
	 * @param comment 数据的comment
	 * @return
	 */
	public CreateTable addColumn(String columnName, int jdbcType,String comment){
		SqlColumn column= new SqlColumn(columnName , jdbcType) ;
		column.setComment(comment);
		addColumn(column) ;

		return this;
	}

	/**
	 * 添加字段
	 * @param columnName 列名称
	 * @param jdbcType 在java.sql.Types类中指定类型
	 * @param comment 数据的comment
	 * @return
	 */
	public CreateTable addColumn(String columnName, int jdbcType,String comment,SqlConstraint constraint){
		ExtendConstraintColumn column= new ExtendConstraintColumn(columnName , jdbcType) ;
		column.setComment(comment);
		column.addSqlConstraint(constraint);
		addColumn(column) ;

		return this;
	}
	
	
	/**
	 * 添加字段
	 * @param columnName 列名称
	 * @param sqlType 数据类型
	 * @return
	 */
	public CreateTable addColumn(String columnName, SqlType sqlType){
		addColumn(new SqlColumn(columnName , sqlType)) ;
		
		return this;
	}
	
	
	/**
	 * 添加字段
	 * @param columnName 列名称
	 * @param sqlType 数据类型
	 * @return
	 */
	public CreateTable addColumn(String columnName, SqlType sqlType,SqlConstraint constraint){
		ExtendConstraintColumn column= new ExtendConstraintColumn(columnName , sqlType) ;
		column.addSqlConstraint(constraint);
		addColumn(column) ;
		
		return this;
	}
	
	/**
	 * 添加字段
	 * @param columnName 列名称
	 * @param sqlType 数据类型
	 * @return
	 */
	public CreateTable addColumn(String columnName, SqlType sqlType,String comment){
		SqlColumn column= new SqlColumn(columnName , sqlType) ;
		column.setComment(comment);
		addColumn(column) ;		
		return this;
	}
	
	/**
	 * 添加字段
	 * @param columnName 列名称
	 * @param sqlType 数据类型
	 * @return
	 */
	public CreateTable addColumn(String columnName, SqlType sqlType,String comment, SqlConstraint constraint){
		ExtendConstraintColumn column= new ExtendConstraintColumn(columnName , sqlType) ;
		column.setComment(comment);
		column.addSqlConstraint(constraint);
		addColumn(column) ;		
		return this;
	}
	
	
	
	/**
	 * 添加字段
	 * @param columnName 列名称
	 * @param jdbcType java.sql.Types类中指定类型
	 * @param length 长度
	 * @return
	 */
	public CreateTable addColumn(String columnName, int jdbcType,int length){
		addColumn(new SqlColumn(columnName , jdbcType)) ;
		
		return this;
	}
	
	/**
	 * 添加字段
	 * @param columnName 列名称
	 * @param jdbcType java.sql.Types类中指定类型
	 * @param length 长度
	 * @return
	 */
	public CreateTable addColumn(String columnName, int jdbcType,int length, SqlConstraint constraint){
		ExtendConstraintColumn column= new ExtendConstraintColumn(columnName , jdbcType, jdbcType) ;
		column.addSqlConstraint(constraint);
		addColumn( column ) ;
		
		return this;
	}
	
	/**
	 * 添加字段
	 * @param columnName 列名称
	 * @param jdbcType java.sql.Types类中指定类型
	 * @param length 长度
	 * @return
	 */
	public CreateTable addColumn(String columnName, int jdbcType,int length,String comment){
		SqlColumn column= new SqlColumn(columnName , jdbcType,length) ;
		column.setComment(comment);
		addColumn(column) ;		
		return this;
	}
	
	/**
	 * 添加字段
	 * @param columnName 列名称
	 * @param jdbcType java.sql.Types类中指定类型
	 * @param length 长度
	 * @return
	 */
	public CreateTable addColumn(String columnName, int jdbcType,int length,String comment, SqlConstraint constraint){
		ExtendConstraintColumn column= new ExtendConstraintColumn(columnName , jdbcType,length) ;
		column.setComment(comment);
		column.addSqlConstraint(constraint);
		addColumn(column) ;		
		return this;
	}

	/**
	 * 添加字段
	 * @param fields
	 */
	public CreateTable addColumn(SqlColumn  ...columns  ){
		for(SqlColumn column  : columns  ){

			this.columns.add(column);
		}
		return this;
	}

	/*
	 * (non-Javadoc)
	 * @see com.honey.core.dom.sql.ddl.Create#compiledContent(int)
	 */
	@Override
	public CharacterBuilder compiledContent(int indentLevel) throws CompilationException {
		CharacterBuilder answer = CharacterBuilderFactory.createC16StringBuilder();
		
		answer.append(super.compiledContent(indentLevel ) );
		answer.append( SqlKeyWord.TABLE );
		
		answer.append(table.compiledContent(0));
		answer.append(SqlKeyWord.OPERATION_PAREN_LEFT);

		indentLevel ++ ; 
		boolean comma = false;
		for(SqlColumn column : columns ){
			if (comma)answer.append(SqlKeyWord.OPERATION_COMMA);else comma = true;
			answer.append(column.compiledContent(indentLevel) );
		}
		
		//添加主键约束
		foramtPrimarykey( getPrimarykeyColumn() ,answer, indentLevel);
		foramtUniquekey( answer, indentLevel);
		
		indentLevel -- ;
		IndentSpace.newSpace(answer, indentLevel);
		answer.append(SqlKeyWord.OPERATION_PAREN_RIGHT);
		if( StringUtility.stringHasValue( append )){
			answer.append( append );
		}
		answer.append(SqlKeyWord.OPERATION_SEMICOLON);
		return answer;
	}


	/**
	 * 主键约束.<br />
	 * <ul>
  	 * <li>主键约束本身也是是唯一约束</li>
  	 * <li>一张表只能有一个主键约束,通常说的双主键或者多主键是指多个列共同组成一个主键约束,这些主键列的值是可以重复的,但是所有主键列的值是不能重复.
  	 * <br />
  	 * 例如: ID 和SID是双主键,也就是 ID 和SID组成双主键约束. <br />
	 *   那么 :<br />
  	 * <table width="200" border="1">
	 * <tr>
	 * <td width="76">ID</td>
	 * <td width="108">SID</td>
	 * </tr>
	 * <tr>
	 * <td>1001</td>
	 * <td>3</td>
	 * </tr>
	 * <tr>
	 * <td>1001</td>
	 * <td>4</td>
	 * </tr>
	 * <tr>
	 * <td>1002</td>
	 * <td>4</td>
	 * </tr>
	 * </table>
	 * <br />成立
	 * </li>
	 * </ul>
	 * @param cb
	 * @param indentLevel
	 * @throws CompilationException 
	 */
	private void foramtPrimarykey(List<ExtendConstraintColumn> eColumns,CharacterBuilder cb  , int indentLevel) throws CompilationException{
		if(eColumns.size()>0 ){
			SqlConstraint  constraint = eColumns.get(0).getConstraint();
			SqlConstraint  c = constraint.getPrimaryKeyConstraint();
			if(c == null) return ;
			cb.append(SqlKeyWord.OPERATION_COMMA);
			IndentSpace.newLine(cb);
			
			cb.append( c.compiledContent(indentLevel));
			cb.append(SqlKeyWord.OPERATION_PAREN_LEFT);
			boolean comma = false;
			for(ExtendConstraintColumn  eColumn : eColumns){
				if (comma)cb.append(SqlKeyWord.OPERATION_COMMA);else comma = true;
				cb.append(eColumn.getName().getValue())
				;
			}
			cb.append(SqlKeyWord.OPERATION_PAREN_RIGHT)
			;
		}
	}


	/**
	 * 添加唯一约束
	 * @param cb
	 * @param indentLevel
	 * @throws CompilationException 
	 */
	private void foramtUniquekey(CharacterBuilder cb  , int indentLevel) throws CompilationException{
		List<ExtendConstraintColumn> eColumns = getUniqueColumn();
		if(eColumns.size()>0 ){
			SqlConstraint  constraint = eColumns.get(0).getConstraint();
			SqlConstraint  c = constraint.getUniqueConstraint();
			if ( c == null ) return ;
			cb.append(SqlKeyWord.OPERATION_COMMA);
			boolean comma = false;
			for(ExtendConstraintColumn eColumn : eColumns  ){
				if (comma)cb.append(SqlKeyWord.OPERATION_COMMA);else comma = true;
				IndentSpace.newLine(cb);
				cb.append( c.compiledContent(indentLevel))
				.append(SqlKeyWord.OPERATION_PAREN_LEFT)
				.append(eColumn.getName().getValue())	
				.append(SqlKeyWord.OPERATION_PAREN_RIGHT)
				;
			}
		}
	}

	/**
	 * 获取所有的主键.<br />
	 * @return
	 */
	private List<ExtendConstraintColumn> getPrimarykeyColumn(){
		List<ExtendConstraintColumn> primarykeyList = new ArrayList<ExtendConstraintColumn>();
		for(SqlColumn column : columns){
			if(column instanceof ExtendConstraintColumn ){
				ExtendConstraintColumn eColumn =(ExtendConstraintColumn)column;
				if(eColumn.getConstraint().isPrimaryKeyConstraint() && ! eColumn.isConstraintInline() ){
					primarykeyList.add(eColumn);
				}
			}
		}

		return primarykeyList;
	}

	
	/**
	 * 获取所有的唯一键
	 * @return
	 */
	private List<ExtendConstraintColumn> getUniqueColumn(){
		List<ExtendConstraintColumn> uniqueList = new ArrayList<ExtendConstraintColumn>();
		for(SqlColumn column : columns){
			if(column instanceof ExtendConstraintColumn ){
				ExtendConstraintColumn eColumn =(ExtendConstraintColumn)column;
				if(eColumn.getConstraint().isUniqueConstraint() && ! eColumn.isConstraintInline()){
					uniqueList.add(eColumn);
				}
			}
		}
		return uniqueList;
	}
}
